/*
 * Copyright (C) 2010-2018 Gordon Fraser, Andrea Arcuri and EvoSuite
 * contributors
 *
 * This file is part of EvoSuite.
 *
 * EvoSuite is free software: you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3.0 of the License, or
 * (at your option) any later version.
 *
 * EvoSuite is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with EvoSuite. If not, see <http://www.gnu.org/licenses/>.
 */
package org.evosuite.testcase.execution;

import org.evosuite.Properties;
import org.evosuite.Properties.Criterion;
import org.evosuite.TestGenerationContext;
import org.evosuite.coverage.branch.Branch;
import org.evosuite.coverage.branch.BranchPool;
import org.evosuite.coverage.dataflow.DefUse;
import org.evosuite.coverage.dataflow.DefUsePool;
import org.evosuite.coverage.dataflow.Definition;
import org.evosuite.coverage.dataflow.Use;
import org.evosuite.setup.CallContext;
import org.evosuite.statistics.RuntimeVariable;
import org.evosuite.utils.ArrayUtil;
import org.objectweb.asm.Opcodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;
import java.util.Map.Entry;

/**
 * Keep a trace of the program execution
 *
 * @author Gordon Fraser
 */
public class ExecutionTraceImpl implements ExecutionTrace, Cloneable {

    @Deprecated
    public static class BranchEval {
        private final int branchId;
        private CallContext context = null;
        private final double falseDistance;
        private final double trueDistance;

        public BranchEval(int branchId, double trueDistance, double falseDistance) {
            this.branchId = branchId;
            this.trueDistance = trueDistance;
            this.falseDistance = falseDistance;
        }

        public BranchEval(int branchId, double trueDistance, double falseDistance, CallContext context) {
            this.branchId = branchId;
            this.trueDistance = trueDistance;
            this.falseDistance = falseDistance;
            this.context = context;
        }

        public int getBranchId() {
            return branchId;
        }

        public CallContext getContext() {
            return context;
        }

        public double getFalseDistance() {
            return falseDistance;
        }

        public double getTrueDistance() {
            return trueDistance;
        }

        @Override
        public String toString() {
            return "BranchEval [branchId=" + branchId + ", trueDistance=" + trueDistance + ", falseDistance="
                    + falseDistance + "]";
        }
    }

    private static final Logger logger = LoggerFactory.getLogger(ExecutionTrace.class);

    /**
     * Constant <code>traceCalls=false</code>
     */
    public static boolean traceCalls = false;

    public static boolean disableContext = false;

    /**
     * Constant <code>traceCoverage=true</code>
     */
    public static boolean traceCoverage = true;

    private static void checkSaneCall(MethodCall call) {
        if (!((call.trueDistanceTrace.size() == call.falseDistanceTrace.size())
                && (call.falseDistanceTrace.size() == call.defuseCounterTrace.size())
                && (call.defuseCounterTrace.size() == call.branchTrace.size()))) {
            throw new IllegalStateException("insane MethodCall: traces should all be of equal size. " + call.explain());
        }

    }

    public static void enableContext() {
        // enableTraceCalls();
        disableContext = false;
    }

    public static void disableContext() {
        disableTraceCalls();
        disableContext = true;
    }

    /**
     * <p>
     * disableTraceCalls
     * </p>
     */
    public static void disableTraceCalls() {
        traceCalls = false;
    }

    /**
     * <p>
     * enableTraceCalls
     * </p>
     */
    public static void enableTraceCalls() {
        traceCalls = true;
    }

    public static boolean isTraceCallsEnabled() {
        return traceCalls;
    }

    /**
     * <p>
     * enableTraceCoverage
     * </p>
     */
    public static void enableTraceCoverage() {
        traceCoverage = true;
    }

    /**
     * Removes from the given ExecutionTrace all finished_calls with an index in
     * removableCalls
     */
    private static void removeFinishCalls(ExecutionTraceImpl trace, ArrayList<Integer> removableCalls) {
        Collections.sort(removableCalls);
        for (int i = removableCalls.size() - 1; i >= 0; i--) {
            int toRemove = removableCalls.get(i);
            MethodCall removed = trace.finishedCalls.remove(toRemove);
            if (removed == null) {
                throw new IllegalStateException("trace.finished_calls not allowed to contain null");
            }
        }
    }

    /**
     * Removes from the given MethodCall all trace information with an index in
     * removableIndices
     */
    private static void removeFromFinishCall(MethodCall call, ArrayList<Integer> removableIndices) {
        checkSaneCall(call);

        Collections.sort(removableIndices);
        for (int i = removableIndices.size() - 1; i >= 0; i--) {
            int removableIndex = removableIndices.get(i);
            Integer removedBranch = call.branchTrace.remove(removableIndex);
            Double removedTrue = call.trueDistanceTrace.remove(removableIndex);
            Double removedFalse = call.falseDistanceTrace.remove(removableIndex);
            Integer removedCounter = call.defuseCounterTrace.remove(removableIndex);
            if ((removedCounter == null) || (removedBranch == null) || (removedTrue == null)
                    || (removedFalse == null)) {
                throw new IllegalStateException("trace.finished_calls-traces not allowed to contain null");
            }
        }
    }

    private List<BranchEval> branchesTrace = new ArrayList<>();

    // Coverage information
    public Map<String, Map<String, Map<Integer, Integer>>> coverage = Collections
            .synchronizedMap(new HashMap<>());

    public Map<Integer, Integer> coveredFalse = Collections.synchronizedMap(new HashMap<>());

    public Map<String, Integer> coveredMethods = Collections.synchronizedMap(new HashMap<>());

    public Map<String, Integer> coveredBranchlessMethods = Collections.synchronizedMap(new HashMap<>());

    public Map<Integer, Integer> coveredPredicates = Collections.synchronizedMap(new HashMap<>());

    public Map<Integer, Integer> coveredTrue = Collections.synchronizedMap(new HashMap<>());

    public Map<Integer, Integer> coveredDefs = Collections.synchronizedMap(new HashMap<>());

    public Map<Integer, Map<CallContext, Double>> coveredTrueContext = Collections
            .synchronizedMap(new HashMap<>());

    public Map<Integer, Map<CallContext, Double>> coveredFalseContext = Collections
            .synchronizedMap(new HashMap<>());

    public Map<Integer, Map<CallContext, Integer>> coveredPredicateContext = Collections
            .synchronizedMap(new HashMap<>());

    public Map<String, Map<CallContext, Integer>> coveredMethodContext = Collections
            .synchronizedMap(new HashMap<>());

    // number of seen Definitions and uses for indexing purposes
    private int duCounter = 0;
    // The last explicitly thrown exception is kept here
    private Throwable explicitException = null;

    public Map<Integer, Double> falseDistances = Collections.synchronizedMap(new HashMap<>());
    private final Map<Integer, Double> falseDistancesSum = Collections.synchronizedMap(new HashMap<>());
    // finished_calls;
    public List<MethodCall> finishedCalls = Collections.synchronizedList(new ArrayList<>());
    public Map<Integer, Object> knownCallerObjects = Collections.synchronizedMap(new HashMap<>());
    // to differentiate between different MethodCalls
    private int methodId = 0;
    public Map<Integer, Double> mutantDistances = Collections.synchronizedMap(new HashMap<>());
    // for defuse-coverage it is important to keep track of all the objects that
    // called the ExecutionTracer
    private int objectCounter = 0;
    // for each Variable-Name these maps hold the data for which objectID
    // at which time (duCounter) which Definition or Use was passed
    public Map<String, HashMap<Integer, HashMap<Integer, Integer>>> passedDefinitions = Collections
            .synchronizedMap(new HashMap<>());
    public Map<String, HashMap<Integer, HashMap<Integer, Integer>>> passedUses = Collections
            .synchronizedMap(new HashMap<>());

    public Map<String, HashMap<Integer, HashMap<Integer, Object>>> passedDefinitionObject = Collections
            .synchronizedMap(new HashMap<>());
    public Map<String, HashMap<Integer, HashMap<Integer, Object>>> passedUseObject = Collections
            .synchronizedMap(new HashMap<>());

    private int proxyCount = 1;
    // Data information
    public Map<String, Map<String, Map<Integer, Integer>>> returnData = Collections
            .synchronizedMap(new HashMap<>());

    // active calls
    LinkedList<MethodCall> stack = new LinkedList<>();

    public Set<Integer> touchedMutants = Collections.synchronizedSet(new HashSet<>());

    public Map<Integer, Double> trueDistances = Collections.synchronizedMap(new HashMap<>());

    private final Map<Integer, Double> trueDistancesSum = Collections.synchronizedMap(new HashMap<>());

    public static Set<Integer> gradientBranches = Collections.synchronizedSet(new HashSet<>());

    public static Set<Integer> gradientBranchesCoveredTrue = Collections.synchronizedSet(new HashSet<>());

    public static Set<Integer> gradientBranchesCoveredFalse = Collections.synchronizedSet(new HashSet<>());

    public static Map<RuntimeVariable, Set<Integer>> bytecodeInstructionReached = Collections
            .synchronizedMap(new HashMap<>());

    public static Map<RuntimeVariable, Set<Integer>> bytecodeInstructionCoveredTrue = Collections
            .synchronizedMap(new HashMap<>());

    public static Map<RuntimeVariable, Set<Integer>> bytecodeInstructionCoveredFalse = Collections
            .synchronizedMap(new HashMap<>());

    /**
     * <p>
     * Constructor for ExecutionTraceImpl.
     * </p>
     */
    public ExecutionTraceImpl() {
        stack.add(new MethodCall("", "", 0, 0, 0)); // Main method
    }

    /**
     * <p>
     * addProxy
     * </p>
     */
    public void addProxy() {
        proxyCount++;
    }

    /**
     * <p>
     * removeProxy
     * </p>
     */
    public void removeProxy() {
        proxyCount--;
    }

    /**
     * {@inheritDoc}
     * <p>
     * Add branch to currently active method call
     */
    @Override
    public void branchPassed(int branch, int bytecode_id, double true_distance, double false_distance) {

        assert (true_distance >= 0.0);
        assert (false_distance >= 0.0);
        updateTopStackMethodCall(branch, bytecode_id, true_distance, false_distance);

        // TODO: property should really be called TRACK_GRADIENT_BRANCHES!
        if (Properties.TRACK_BOOLEAN_BRANCHES) {
            if ((true_distance != 0 && true_distance != 1) || (false_distance != 0 && false_distance != 1))
                gradientBranches.add(branch);
        }

        if (traceCoverage) {
            if (!coveredPredicates.containsKey(branch))
                coveredPredicates.put(branch, 1);
            else
                coveredPredicates.put(branch, coveredPredicates.get(branch) + 1);

            if (true_distance == 0.0) {
                if (!coveredTrue.containsKey(branch))
                    coveredTrue.put(branch, 1);
                else
                    coveredTrue.put(branch, coveredTrue.get(branch) + 1);

            }

            if (false_distance == 0.0) {
                if (!coveredFalse.containsKey(branch))
                    coveredFalse.put(branch, 1);
                else
                    coveredFalse.put(branch, coveredFalse.get(branch) + 1);
            }
        }

        if (Properties.TRACK_COVERED_GRADIENT_BRANCHES) {
            if (gradientBranches.contains(branch)) {
                if ((coveredTrue.containsKey(branch)))
                    gradientBranchesCoveredTrue.add(branch);
                if ((coveredFalse.containsKey(branch)))
                    gradientBranchesCoveredFalse.add(branch);
            }
        }

        if (Properties.BRANCH_COMPARISON_TYPES) {
            int opcode = BranchPool.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT())
                    .getBranch(branch).getInstruction().getASMNode().getOpcode();
            int previousOpcode = -2;
            if (BranchPool.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT()).getBranch(branch)
                    .getInstruction().getASMNode().getPrevious() != null)
                previousOpcode = BranchPool.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT())
                        .getBranch(branch).getInstruction().getASMNode().getPrevious().getOpcode();
            boolean cTrue = coveredTrue.containsKey(branch);
            boolean cFalse = coveredFalse.containsKey(branch);
            switch (previousOpcode) {
                case Opcodes.LCMP:
                    trackBranchOpcode(bytecodeInstructionReached, RuntimeVariable.Reached_lcmp, branch);
                    if (cTrue)
                        trackBranchOpcode(bytecodeInstructionCoveredTrue, RuntimeVariable.Covered_lcmp, branch);
                    if (cFalse)
                        trackBranchOpcode(bytecodeInstructionCoveredFalse, RuntimeVariable.Covered_lcmp, branch);
                    break;
                case Opcodes.FCMPL:
                    trackBranchOpcode(bytecodeInstructionReached, RuntimeVariable.Reached_fcmpl, branch);
                    if (cTrue)
                        trackBranchOpcode(bytecodeInstructionCoveredTrue, RuntimeVariable.Covered_fcmpl, branch);
                    if (cFalse)
                        trackBranchOpcode(bytecodeInstructionCoveredFalse, RuntimeVariable.Covered_fcmpl, branch);
                    break;
                case Opcodes.FCMPG:
                    trackBranchOpcode(bytecodeInstructionReached, RuntimeVariable.Reached_fcmpg, branch);
                    if (cTrue)
                        trackBranchOpcode(bytecodeInstructionCoveredTrue, RuntimeVariable.Covered_fcmpg, branch);
                    if (cFalse)
                        trackBranchOpcode(bytecodeInstructionCoveredFalse, RuntimeVariable.Covered_fcmpg, branch);
                    break;
                case Opcodes.DCMPL:
                    trackBranchOpcode(bytecodeInstructionReached, RuntimeVariable.Reached_dcmpl, branch);
                    if (cTrue)
                        trackBranchOpcode(bytecodeInstructionCoveredTrue, RuntimeVariable.Covered_dcmpl, branch);
                    if (cFalse)
                        trackBranchOpcode(bytecodeInstructionCoveredFalse, RuntimeVariable.Covered_dcmpl, branch);
                    break;
                case Opcodes.DCMPG:
                    trackBranchOpcode(bytecodeInstructionReached, RuntimeVariable.Reached_dcmpg, branch);
                    if (cTrue)
                        trackBranchOpcode(bytecodeInstructionCoveredTrue, RuntimeVariable.Covered_dcmpg, branch);
                    if (cFalse)
                        trackBranchOpcode(bytecodeInstructionCoveredFalse, RuntimeVariable.Covered_dcmpg, branch);
                    break;
            }
            switch (opcode) {
                // copmpare int with zero
                case Opcodes.IFEQ:
                case Opcodes.IFNE:
                case Opcodes.IFLT:
                case Opcodes.IFGE:
                case Opcodes.IFGT:
                case Opcodes.IFLE:
                    trackBranchOpcode(bytecodeInstructionReached, RuntimeVariable.Reached_IntZero, branch);
                    if (cTrue)
                        trackBranchOpcode(bytecodeInstructionCoveredTrue, RuntimeVariable.Covered_IntZero, branch);
                    if (cFalse)
                        trackBranchOpcode(bytecodeInstructionCoveredFalse, RuntimeVariable.Covered_IntZero, branch);
                    break;
                // copmpare int with int
                case Opcodes.IF_ICMPEQ:
                case Opcodes.IF_ICMPNE:
                case Opcodes.IF_ICMPLT:
                case Opcodes.IF_ICMPGE:
                case Opcodes.IF_ICMPGT:
                case Opcodes.IF_ICMPLE:
                    trackBranchOpcode(bytecodeInstructionReached, RuntimeVariable.Reached_IntInt, branch);
                    if (cTrue)
                        trackBranchOpcode(bytecodeInstructionCoveredTrue, RuntimeVariable.Covered_IntInt, branch);
                    if (cFalse)
                        trackBranchOpcode(bytecodeInstructionCoveredFalse, RuntimeVariable.Covered_IntInt, branch);
                    break;
                // copmpare reference with reference
                case Opcodes.IF_ACMPEQ:
                case Opcodes.IF_ACMPNE:
                    trackBranchOpcode(bytecodeInstructionReached, RuntimeVariable.Reached_RefRef, branch);
                    if (cTrue)
                        trackBranchOpcode(bytecodeInstructionCoveredTrue, RuntimeVariable.Covered_RefRef, branch);
                    if (cFalse)
                        trackBranchOpcode(bytecodeInstructionCoveredFalse, RuntimeVariable.Covered_RefRef, branch);
                    break;
                // compare reference with null
                case Opcodes.IFNULL:
                case Opcodes.IFNONNULL:
                    trackBranchOpcode(bytecodeInstructionReached, RuntimeVariable.Reached_RefNull, branch);
                    if (cTrue)
                        trackBranchOpcode(bytecodeInstructionCoveredTrue, RuntimeVariable.Covered_RefNull, branch);
                    if (cFalse)
                        trackBranchOpcode(bytecodeInstructionCoveredFalse, RuntimeVariable.Covered_RefNull, branch);
                    break;

            }
        }

        if (!trueDistances.containsKey(branch))
            trueDistances.put(branch, true_distance);
        else
            trueDistances.put(branch, Math.min(trueDistances.get(branch), true_distance));

        if (!falseDistances.containsKey(branch))
            falseDistances.put(branch, false_distance);
        else
            falseDistances.put(branch, Math.min(falseDistances.get(branch), false_distance));

        if (!trueDistancesSum.containsKey(branch))
            trueDistancesSum.put(branch, true_distance);
        else
            trueDistancesSum.put(branch, trueDistancesSum.get(branch) + true_distance);

        if (!falseDistancesSum.containsKey(branch))
            falseDistancesSum.put(branch, false_distance);
        else
            falseDistancesSum.put(branch, falseDistancesSum.get(branch) + false_distance);

        if (!disableContext && (Properties.INSTRUMENT_CONTEXT || Properties.INSTRUMENT_METHOD_CALLS
                || ArrayUtil.contains(Properties.CRITERION, Criterion.IBRANCH)
                || ArrayUtil.contains(Properties.CRITERION, Criterion.CBRANCH))) {
            updateBranchContextMaps(branch, true_distance, false_distance);
        }

        // This requires a lot of memory and should not really be used
        if (Properties.BRANCH_EVAL) {
            branchesTrace.add(new BranchEval(branch, true_distance, false_distance));
        }
    }

    /**
     * Track reach/coverage of branch based on it's underlying opcode during
     * execution
     *
     * @param trackedMap relevant map for the variable type (one of the three static
     *                   maps)
     * @param v          branch type (based on opcode)
     * @param branch_id  of the tracked branch
     */
    private void trackBranchOpcode(Map<RuntimeVariable, Set<Integer>> trackedMap, RuntimeVariable v, int branch_id) {
        if (!trackedMap.containsKey(v))
            trackedMap.put(v, new HashSet<>());
        Set<Integer> branchSet = trackedMap.get(v);
        branchSet.add(branch_id);
        trackedMap.put(v, branchSet);
    }

    /**
     * @param branch
     * @param true_distance
     * @param false_distance
     */
    private void updateBranchContextMaps(int branch, double true_distance, double false_distance) {
        if (!coveredPredicateContext.containsKey(branch)) {
            coveredPredicateContext.put(branch, new HashMap<>());
            coveredTrueContext.put(branch, new HashMap<>());
            coveredFalseContext.put(branch, new HashMap<>());
        }
        //CallContext context = new CallContext(new Throwable().getStackTrace());
        CallContext context = new CallContext(stack);

        if (!coveredPredicateContext.get(branch).containsKey(context)) {
            coveredPredicateContext.get(branch).put(context, 1);
            coveredTrueContext.get(branch).put(context, true_distance);
            coveredFalseContext.get(branch).put(context, false_distance);
        } else {
            coveredPredicateContext.get(branch).put(context, coveredPredicateContext.get(branch).get(context) + 1);
            coveredTrueContext.get(branch).put(context,
                    Math.min(coveredTrueContext.get(branch).get(context), true_distance));
            coveredFalseContext.get(branch).put(context,
                    Math.min(coveredFalseContext.get(branch).get(context), false_distance));
        }
    }

    /**
     * {@inheritDoc}
     * <p>
     * Reset to 0
     */
    @Override
    public void clear() {
        finishedCalls = new ArrayList<>();
        stack = new LinkedList<>();

        // stack.clear();
        // finished_calls.clear();
        stack.add(new MethodCall("", "", 0, 0, 0)); // Main method
        coverage = new HashMap<>();
        returnData = new HashMap<>();

        methodId = 0;
        duCounter = 0;
        objectCounter = 0;
        knownCallerObjects = new HashMap<>();
        trueDistances = new HashMap<>();
        falseDistances = new HashMap<>();
        mutantDistances = new HashMap<>();
        touchedMutants = new HashSet<>();
        coveredMethods = new HashMap<>();
        coveredBranchlessMethods = new HashMap<>();
        coveredPredicates = new HashMap<>();
        coveredTrue = new HashMap<>();
        coveredFalse = new HashMap<>();
        coveredDefs = new HashMap<>();
        passedDefinitions = new HashMap<>();
        passedUses = new HashMap<>();
        passedDefinitionObject = new HashMap<>();
        passedUseObject = new HashMap<>();
        branchesTrace = new ArrayList<>();
        coveredTrueContext = new HashMap<>();
        coveredFalseContext = new HashMap<>();
        coveredPredicateContext = new HashMap<>();

        initializedClasses = new ArrayList<>();
        classesWithStaticReads = new HashSet<>();
        classesWithStaticWrites = new HashSet<>();
    }

    /**
     * {@inheritDoc}
     * <p>
     * Create a deep copy
     */
    @Override
    public ExecutionTraceImpl clone() {

        ExecutionTraceImpl copy = new ExecutionTraceImpl();
        for (MethodCall call : finishedCalls) {
            copy.finishedCalls.add(call.clone());
        }
        // copy.finished_calls.addAll(finished_calls);
        copy.coverage = new HashMap<>();
        if (coverage != null) {
            copy.coverage.putAll(coverage);
        }
        copy.returnData = new HashMap<>();
        copy.returnData.putAll(returnData);
        /*
         * if(stack != null && !stack.isEmpty() && stack.peek().method_name !=
         * null && stack.peek().method_name.equals("")) { logger.info(
         * "Copying main method"); copy.finished_calls.add(stack.peek()); }
         */
        copy.trueDistances.putAll(trueDistances);
        copy.falseDistances.putAll(falseDistances);
        copy.coveredMethods.putAll(coveredMethods);
        copy.coveredBranchlessMethods.putAll(coveredBranchlessMethods);
        copy.coveredPredicates.putAll(coveredPredicates);
        copy.coveredTrue.putAll(coveredTrue);
        copy.coveredFalse.putAll(coveredFalse);
        copy.coveredDefs.putAll(coveredDefs);
        copy.touchedMutants.addAll(touchedMutants);
        copy.mutantDistances.putAll(mutantDistances);
        copy.passedDefinitions.putAll(passedDefinitions);
        copy.passedUses.putAll(passedUses);
        copy.passedDefinitionObject.putAll(passedDefinitionObject);
        copy.passedUseObject.putAll(passedUseObject);
        copy.branchesTrace.addAll(branchesTrace);

        copy.coveredTrueContext.putAll(coveredTrueContext);
        copy.coveredFalseContext.putAll(coveredFalseContext);
        copy.coveredPredicateContext.putAll(coveredPredicateContext);

        copy.initializedClasses.addAll(initializedClasses);
        copy.classesWithStaticReads.addAll(classesWithStaticReads);
        copy.classesWithStaticWrites.addAll(classesWithStaticWrites);

        copy.methodId = methodId;
        copy.duCounter = duCounter;
        copy.objectCounter = objectCounter;
        copy.knownCallerObjects.putAll(knownCallerObjects);
        copy.proxyCount = 1;
        return copy;
    }

    /**
     * {@inheritDoc}
     * <p>
     * Adds Definition-Use-Coverage trace information for the given definition.
     * <p>
     * Registers the given caller-Object Traces the occurrence of the given
     * definition in the passedDefs-field Sets the given definition as the
     * currently active one for the definitionVariable in the
     * activeDefinitions-field Adds fake trace information to the currently
     * active MethodCall in this.stack
     */
    @Override
    public void definitionPassed(Object object, Object caller, int defID) {

        if (!traceCalls) {
            return;
        }

        Definition def = DefUsePool.getDefinitionByDefId(defID);
        if (def == null) {
            throw new IllegalStateException("expect DefUsePool to known defIDs that are passed by instrumented code");
        }
        if (!coveredDefs.containsKey(defID)) {
            coveredDefs.put(defID, 0);
        } else {
            coveredDefs.put(defID, coveredDefs.get(defID) + 1);
        }
        String varName = def.getVariableName();

        int objectID = registerObject(caller);

        // if this is a static variable, treat objectID as zero for consistency
        // in the representation of static data
        if (objectID != 0 && def.isStaticDefUse())
            objectID = 0;
        if (passedDefinitions.get(varName) == null) {
            passedDefinitions.put(varName, new HashMap<>());
            passedDefinitionObject.put(varName, new HashMap<>());
        }
        HashMap<Integer, Integer> defs = passedDefinitions.get(varName).get(objectID);
        HashMap<Integer, Object> defsObject = passedDefinitionObject.get(varName).get(objectID);
        if (defs == null) {
            defs = new HashMap<>();
            defsObject = new HashMap<>();
        }
        defs.put(duCounter, defID);
        defsObject.put(duCounter, object);
        passedDefinitions.get(varName).put(objectID, defs);
        passedDefinitionObject.get(varName).put(objectID, defsObject);

        // logger.trace(duCounter+": set active definition for var
        // "+def.getDUVariableName()+" on object "+objectID+" to Def "+defID);
        duCounter++;
    }

    /**
     * {@inheritDoc}
     * <p>
     * Add a new method call to stack
     */
    @Override
    public void enteredMethod(String className, String methodName, Object caller) {
        if (traceCoverage) {
            String id = className + "." + methodName;
            if (!coveredMethods.containsKey(id)) {
                coveredMethods.put(id, 1);
            } else {
                coveredMethods.put(id, coveredMethods.get(id) + 1);
            }

            if (BranchPool.getInstance(TestGenerationContext.getInstance().getClassLoaderForSUT())
                    .isBranchlessMethod(className, id)) {
                if (!coveredBranchlessMethods.containsKey(id)) {
                    coveredBranchlessMethods.put(id, 1);
                } else {
                    coveredBranchlessMethods.put(id, coveredBranchlessMethods.get(id) + 1);
                }
            }
        }
        if (!className.isEmpty() && !methodName.isEmpty()) {
            int callingObjectID = registerObject(caller);
            MethodCall call = new MethodCall(className, methodName, methodId, callingObjectID, stack.size());
            methodId++;
            // TODO: Skip this?
            if (traceCalls) {
                if (ArrayUtil.contains(Properties.CRITERION, Criterion.DEFUSE)
                        || ArrayUtil.contains(Properties.CRITERION, Criterion.ALLDEFS)) {
                    call.branchTrace.add(-1);
                    call.trueDistanceTrace.add(1.0);
                    call.falseDistanceTrace.add(0.0);
                    call.defuseCounterTrace.add(duCounter);
                    // TODO line_trace ?
                }
            }
            stack.push(call);

            if (!disableContext
                    && (Properties.INSTRUMENT_CONTEXT || ArrayUtil.contains(Properties.CRITERION, Criterion.IBRANCH)
                    || ArrayUtil.contains(Properties.CRITERION, Criterion.CBRANCH))) {
                updateMethodContextMaps(className, methodName, caller);
            }
        }
    }

    /**
     * @param className
     * @param methodName
     * @param caller
     */
    private void updateMethodContextMaps(String className, String methodName, Object caller) {
        String id = className + "." + methodName;
        if (!coveredMethodContext.containsKey(id)) {
            coveredMethodContext.put(id, new HashMap<>());
        }

        // CallContext context = new CallContext(new Throwable().getStackTrace());
        CallContext context = new CallContext(stack);

        if (!coveredMethodContext.get(id).containsKey(context)) {
            coveredMethodContext.get(id).put(context, 1);
        } else {
            coveredMethodContext.get(id).put(context, coveredMethodContext.get(id).get(context) + 1);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        ExecutionTraceImpl other = (ExecutionTraceImpl) obj;
        if (coverage == null) {
            if (other.coverage != null) {
                return false;
            }
        } else if (!coverage.equals(other.coverage)) {
            return false;
        }
        if (finishedCalls == null) {
            if (other.finishedCalls != null) {
                return false;
            }
        } else if (!finishedCalls.equals(other.finishedCalls)) {
            return false;
        }
        if (returnData == null) {
            if (other.returnData != null) {
                return false;
            }
        } else if (!returnData.equals(other.returnData)) {
            return false;
        }
        if (stack == null) {
            return other.stack == null;
        } else return stack.equals(other.stack);
    }

    /**
     * {@inheritDoc}
     * <p>
     * Pop last method call from stack
     */
    @Override
    public void exitMethod(String classname, String methodname) {
        if (!classname.isEmpty() && !methodname.isEmpty()) {
            // if(traceCalls) {
            if (!stack.isEmpty() && !(stack.peek().methodName.equals(methodname))) {
                // Handle cases where unexpected calls are on the stack
                if (stack.peek().methodName.isEmpty() && !stack.peek().branchTrace.isEmpty()) {
                    finishedCalls.add(stack.pop());
                } else {
                    // Usually, this happens if we use mutation testing and
                    // the mutation causes an unexpected exception or
                    // timeout
                    stack.pop();
                }
            } else {
                finishedCalls.add(stack.pop());
            }
            //}
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized void finishCalls() {
        logger.debug("At the end, we have " + stack.size() + " calls left on stack");
        while (!stack.isEmpty()) {
            finishedCalls.add(stack.pop());
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public List<BranchEval> getBranchesTrace() {
        return branchesTrace;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getCoverageData()
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<String, Map<String, Map<Integer, Integer>>> getCoverageData() {
        return coverage;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getCoveredFalseBranches()
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public Set<Integer> getCoveredFalseBranches() {
        Set<Integer> covered = new HashSet<>();
        for (Entry<Integer, Double> entry : falseDistances.entrySet()) {
            if (entry.getValue() == 0.0)
                covered.add(entry.getKey());
        }

        return covered;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getCoveredLines()
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public Set<Integer> getCoveredLines(String className) {
        Set<Integer> coveredLines = new HashSet<>();
        for (Entry<String, Map<String, Map<Integer, Integer>>> entry : coverage.entrySet()) {
            if ((entry.getKey().equals(className)) ||
                    // is it a internal class of 'className' ?
                    (entry.getKey().startsWith(className + "$"))) {
                for (Map<Integer, Integer> methodentry : entry.getValue().values()) {
                    coveredLines.addAll(methodentry.keySet());
                }
            }
        }
        return coveredLines;
    }

    @Override
    public Set<Integer> getCoveredLines() {
        return this.getCoveredLines(Properties.TARGET_CLASS);
    }

    @Override
    public Set<Integer> getAllCoveredLines() {
        Set<Integer> coveredLines = new HashSet<>();
        for (Entry<String, Map<String, Map<Integer, Integer>>> entry : coverage.entrySet()) {
            for (Map<Integer, Integer> methodentry : entry.getValue().values()) {
                coveredLines.addAll(methodentry.keySet());
            }
        }
        return coveredLines;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getCoveredMethods()
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public Set<String> getCoveredMethods() {
        return coveredMethods.keySet();
    }

    @Override
    public Set<String> getCoveredBranchlessMethods() {
        return coveredBranchlessMethods.keySet();
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getCoveredPredicates()
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public Set<Integer> getCoveredPredicates() {
        return coveredPredicates.keySet();
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getCoveredTrueBranches()
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public Set<Integer> getCoveredTrueBranches() {
        Set<Integer> covered = new HashSet<>();
        for (Entry<Integer, Double> entry : trueDistances.entrySet()) {
            if (entry.getValue() == 0.0)
                covered.add(entry.getKey());
        }

        return covered;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getCoveredDefinitions()
     */
    @Override
    public Set<Integer> getCoveredDefinitions() {
        return coveredDefs.keySet();
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getDefinitionExecutionCount()
     */
    @Override
    public Map<Integer, Integer> getDefinitionExecutionCount() {
        return coveredDefs;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getDefinitionData()
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<String, HashMap<Integer, HashMap<Integer, Integer>>> getDefinitionData() {
        return passedDefinitions;
    }

    public Map<String, HashMap<Integer, HashMap<Integer, Object>>> getDefinitionDataObjects() {
        return passedDefinitionObject;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Throwable getExplicitException() {
        return explicitException;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getFalseDistance(int)
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public double getFalseDistance(int branchId) {
        return falseDistances.get(branchId);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getFalseDistances()
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<Integer, Double> getFalseDistances() {
        return falseDistances;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getMethodCalls()
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public List<MethodCall> getMethodCalls() {
        return finishedCalls;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getMethodExecutionCount()
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<String, Integer> getMethodExecutionCount() {
        return coveredMethods;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getMutationDistance(int)
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public double getMutationDistance(int mutationId) {
        return mutantDistances.get(mutationId);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getMutationDistances()
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<Integer, Double> getMutationDistances() {
        return mutantDistances;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getPassedDefinitions(java.lang.
     * String)
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<Integer, HashMap<Integer, Integer>> getPassedDefinitions(String variableName) {
        return passedDefinitions.get(variableName);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getPassedUses(java.lang.String)
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<Integer, HashMap<Integer, Integer>> getPassedUses(String variableName) {
        return passedUses.get(variableName);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getPredicateExecutionCount()
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<Integer, Integer> getPredicateExecutionCount() {
        return coveredPredicates;
    }

    /**
     * <p>
     * Getter for the field <code>proxyCount</code>.
     * </p>
     *
     * @return a int.
     */
    public int getProxyCount() {
        return proxyCount;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getReturnData()
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<String, Map<String, Map<Integer, Integer>>> getReturnData() {
        return returnData;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getTouchedMutants()
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public Set<Integer> getTouchedMutants() {
        return touchedMutants;
    }

    @Override
    public Set<Integer> getInfectedMutants() {
        Set<Integer> infectedMutants = new LinkedHashSet<>();
        for (Entry<Integer, Double> entry : mutantDistances.entrySet()) {
            if (entry.getValue() == 0.0) {
                infectedMutants.add(entry.getKey());
            }
        }
        return infectedMutants;
    }

    /**
     * {@inheritDoc}
     * <p>
     * Returns a copy of this trace where all MethodCall-information traced from
     * objects other then the one identified by the given objectID is removed
     * from the finished_calls-field
     * <p>
     * WARNING: this will not affect this.true_distances and other fields of
     * ExecutionTrace this only affects the finished_calls field (which should
     * suffice for BranchCoverageFitness-calculation)
     */
    @Override
    public ExecutionTrace getTraceForObject(int objectId) {
        ExecutionTraceImpl r = clone();
        ArrayList<Integer> removableCalls = new ArrayList<>();
        for (int i = 0; i < r.finishedCalls.size(); i++) {
            MethodCall call = r.finishedCalls.get(i);
            if ((call.callingObjectID != objectId) && (call.callingObjectID != 0)) {
                removableCalls.add(i);
            }
        }
        removeFinishCalls(r, removableCalls);
        return new ExecutionTraceProxy(r);
    }

    /**
     * {@inheritDoc}
     * <p>
     * Returns a copy of this trace where all MethodCall-information associated
     * with duCounters outside the range of the given duCounter-Start and -End
     * is removed from the finished_calls-traces
     * <p>
     * finished_calls without any point in the trace at which the given
     * duCounter range is hit are removed completely
     * <p>
     * Also traces for methods other then the one that holds the given targetDU
     * are removed as well as trace information that would pass the branch of
     * the given targetDU If wantToCoverTargetDU is false instead those
     * targetDUBranch information is removed that would pass the alternative
     * branch of targetDU
     * <p>
     * The latter is because this method only gets called when the given
     * targetDU was not active in the given duCounter-range if and only if
     * wantToCoverTargetDU is set, and since useFitness calculation is on branch
     * level and the branch of the targetDU can be passed before the targetDU is
     * passed this can lead to a flawed branchFitness.
     * <p>
     * <p>
     * WARNING: this will not affect this.true_distances and other fields of
     * ExecutionTrace this only affects the finished_calls field (which should
     * suffice for BranchCoverageFitness-calculation)
     */
    @Override
    public ExecutionTrace getTraceInDUCounterRange(DefUse targetDU, boolean wantToCoverTargetDU, int duCounterStart,
                                                   int duCounterEnd) {

        if (duCounterStart > duCounterEnd) {
            throw new IllegalArgumentException("start has to be lesser or equal end");
            /*
             * // DONE: bug // this still has a major flaw: s.
             * MeanTestClass.mean(): // right now its like we map branches to
             * activeDefenitions // but especially in the root branch of a
             * method // activeDefenitions change during execution time // FIX:
             * in order to avoid these false positives remove all information //
             * for a certain branch if some information for that branch is
             * supposed to be removed // subTodo since branchPassed() only gets
             * called when a branch is passed initially // fake calls to
             * branchPassed() have to be made whenever a DU is passed // s.
             * definitionPassed(), usePassed() and
             * addFakeActiveMethodCallInformation()
             *
             * // DONE: new bug // turns out thats an over-approximation that
             * makes it // impossible to cover some potentially coverable goals
             *
             * // completely new: // if your definition gets overwritten in a
             * trace // the resulting fitness should be the fitness of not
             * taking the branch with the overwriting definition // DONE: in
             * order to do that don't remove older trace information for an
             * overwritten branch // but rather set the true and false distance
             * of that previous branch information to the distance of not taking
             * the overwriting branch // done differently: s.
             * DefUseCoverageTestFitness.getFitness()
             */
        }

        ExecutionTraceImpl r = clone();
        Branch targetDUBranch = targetDU.getControlDependentBranch();
        ArrayList<Integer> removableCalls = new ArrayList<>();
        for (int callPos = 0; callPos < r.finishedCalls.size(); callPos++) {
            MethodCall call = r.finishedCalls.get(callPos);
            // check if call is for the method of targetDU
            if (!call.methodName.equals(targetDU.getMethodName())) {
                removableCalls.add(callPos);
                continue;
            }
            ArrayList<Integer> removableIndices = new ArrayList<>();
            for (int i = 0; i < call.defuseCounterTrace.size(); i++) {
                int currentDUCounter = call.defuseCounterTrace.get(i);
                int currentBranchBytecode = call.branchTrace.get(i);

                if (currentDUCounter < duCounterStart || currentDUCounter > duCounterEnd)
                    removableIndices.add(i);
                else if (currentBranchBytecode == targetDUBranch.getInstruction().getInstructionId()) {
                    // only remove this point in the trace if it would cover
                    // targetDU
                    boolean targetExpressionValue = targetDU.getControlDependentBranchExpressionValue();
                    if (targetExpressionValue) {
                        if (call.trueDistanceTrace.get(i) == 0.0)
                            removableIndices.add(i);
                    } else {
                        if (call.falseDistanceTrace.get(i) == 0.0)
                            removableIndices.add(i);
                    }

                }
            }
            removeFromFinishCall(call, removableIndices);
            if (call.defuseCounterTrace.size() == 0)
                removableCalls.add(callPos);
        }
        removeFinishCalls(r, removableCalls);
        return new ExecutionTraceProxy(r);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getTrueDistance(int)
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public double getTrueDistance(int branchId) {
        return trueDistances.get(branchId);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getTrueDistances()
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<Integer, Double> getTrueDistances() {
        return trueDistances;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getUseData()
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<String, HashMap<Integer, HashMap<Integer, Integer>>> getUseData() {
        return passedUses;
    }

    public Map<String, HashMap<Integer, HashMap<Integer, Object>>> getUseDataObjects() {
        return passedUseObject;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#wasCoveredFalse(int)
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean hasFalseDistance(int predicateId) {
        return falseDistances.containsKey(predicateId);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((coverage == null) ? 0 : coverage.hashCode());
        result = prime * result + ((finishedCalls == null) ? 0 : finishedCalls.hashCode());
        result = prime * result + ((returnData == null) ? 0 : returnData.hashCode());
        result = prime * result + ((stack == null) ? 0 : stack.hashCode());
        return result;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#wasCoveredTrue(int)
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean hasTrueDistance(int predicateId) {
        return trueDistances.containsKey(predicateId);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#lazyClone()
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public ExecutionTrace lazyClone() {
        // TODO Auto-generated method stub
        return null;
    }

    private boolean stackHasMethod(String methodName) {
        for (MethodCall call : stack) {
            if (call.methodName.equals(methodName))
                return true;
        }

        return false;
    }

    /**
     * {@inheritDoc}
     * <p>
     * Add line to currently active method call
     */
    @Override
    public void linePassed(String className, String methodName, int line) {
        if (traceCalls) {
            if (stack.isEmpty()) {
                logger.info("Method stack is empty: " + className + "." + methodName + " - l" + line); // TODO
                // switch
                // back
                // logger.debug to
                // logger.warn
            } else {
                boolean empty = false;
                if (!stack.peek().methodName.equals(methodName)) {
                    if (stack.peek().methodName.equals(""))
                        return;

                    if (stackHasMethod(methodName)) {
                        do {
                            logger.debug("Popping method " + stack.peek().methodName + " because we were looking for "
                                    + methodName);
                            finishedCalls.add(stack.pop());
                        } while (!stack.isEmpty() && !stack.peek().methodName.equals(methodName)
                                && !stack.peek().methodName.equals(""));
                    } else {

                        logger.warn("Popping method " + stack.peek().methodName + " because we were looking for "
                                + methodName);
                        logger.warn("Current stack: " + stack);
                        finishedCalls.add(stack.pop());
                    }
                    if (stack.isEmpty()) {
                        logger.warn("Method stack is empty: " + className + "." + methodName + " - l" + line); // TODO
                        // switch
                        // back
                        empty = true;
                    }
                }
                if (!empty)
                    stack.peek().lineTrace.add(line);
            }
        }
        if (traceCoverage) {
            if (!coverage.containsKey(className)) {
                coverage.put(className, new HashMap<>());
            }

            if (!coverage.get(className).containsKey(methodName)) {
                coverage.get(className).put(methodName, new HashMap<>());
            }

            if (!coverage.get(className).get(methodName).containsKey(line)) {
                coverage.get(className).get(methodName).put(line, 1);
            } else {
                coverage.get(className).get(methodName).put(line,
                        coverage.get(className).get(methodName).get(line) + 1);
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void mutationPassed(int mutationId, double distance) {

        touchedMutants.add(mutationId);
        if (!mutantDistances.containsKey(mutationId)) {
            mutantDistances.put(mutationId, distance);
        } else {
            mutantDistances.put(mutationId, Math.min(distance, mutantDistances.get(mutationId)));
        }
    }

    /**
     * Returns the objecectId for the given object.
     * <p>
     * The ExecutionTracer keeps track of all objects it gets called from in
     * order to distinguish them later in the fitness calculation for the
     * defuse-Coverage-Criterion.
     */
    private int registerObject(Object caller) {
        if (caller == null) {
            return 0;
        }
        for (Integer objectId : knownCallerObjects.keySet()) {
            if (knownCallerObjects.get(objectId) == caller) {
                return objectId;
            }
        }
        // object unknown so far
        objectCounter++;
        knownCallerObjects.put(objectCounter, caller);
        return objectCounter;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void returnValue(String className, String methodName, int value) {
        if (!returnData.containsKey(className)) {
            returnData.put(className, new HashMap<>());
        }

        if (!returnData.get(className).containsKey(methodName)) {
            returnData.get(className).put(methodName, new HashMap<>());
        }

        if (!returnData.get(className).get(methodName).containsKey(value)) {
            // logger.info("Got return value "+value);
            returnData.get(className).get(methodName).put(value, 1);
        } else {
            // logger.info("Got return value again "+value);
            returnData.get(className).get(methodName).put(value,
                    returnData.get(className).get(methodName).get(value) + 1);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setExplicitException(Throwable explicitException) {
        this.explicitException = explicitException;
    }

    /**
     * {@inheritDoc}
     * <p>
     * Returns a String containing the information in passedDefs and passedUses
     * <p>
     * Used for Definition-Use-Coverage-debugging
     */
    @Override
    public String toDefUseTraceInformation() {
        StringBuffer r = new StringBuffer();
        for (String var : passedDefinitions.keySet()) {
            r.append("  for variable: " + var + ": ");
            for (Integer objectId : passedDefinitions.get(var).keySet()) {
                if (passedDefinitions.get(var).keySet().size() > 1) {
                    r.append("\n\ton object " + objectId + ": ");
                }
                r.append(toDefUseTraceInformation(var, objectId));
            }
            r.append("\n  ");
        }
        return r.toString();
    }

    /**
     * {@inheritDoc}
     * <p>
     * Returns a String containing the information in passedDefs and passedUses
     * filtered for a specific variable
     * <p>
     * Used for Definition-Use-Coverage-debugging
     */
    @Override
    public String toDefUseTraceInformation(String targetVar) {
        StringBuffer r = new StringBuffer();
        for (Integer objectId : passedDefinitions.get(targetVar).keySet()) {
            if (passedDefinitions.get(targetVar).keySet().size() > 1) {
                r.append("\n\ton object " + objectId + ": ");
            }
            r.append(toDefUseTraceInformation(targetVar, objectId));
        }
        r.append("\n  ");

        return r.toString();
    }

    /**
     * {@inheritDoc}
     * <p>
     * Returns a String containing the information in passedDefs and passedUses
     * for the given variable
     * <p>
     * Used for Definition-Use-Coverage-debugging
     */
    @Override
    public String toDefUseTraceInformation(String var, int objectId) {
        if (passedDefinitions.get(var) == null) {
            return "";
        }
        if ((objectId == -1) && (passedDefinitions.get(var).keySet().size() == 1)) {
            objectId = (Integer) passedDefinitions.get(var).keySet().toArray()[0];
        }
        if (passedDefinitions.get(var).get(objectId) == null) {
            return "";
        }
        // gather all DUs
        String[] duTrace = new String[this.duCounter];
        for (int i = 0; i < this.duCounter; i++) {
            duTrace[i] = "";
        }
        for (Integer duPos : passedDefinitions.get(var).get(objectId).keySet()) {
            duTrace[duPos] = "(" + duPos + ":Def " + passedDefinitions.get(var).get(objectId).get(duPos) + ")";
        }
        if ((passedUses.get(var) != null) && (passedUses.get(var).get(objectId) != null)) {
            for (Integer duPos : passedUses.get(var).get(objectId).keySet()) {
                duTrace[duPos] = "(" + duPos + ":Use " + passedUses.get(var).get(objectId).get(duPos) + ")";
            }
        }
        // build up the String
        StringBuffer r = new StringBuffer();
        for (String s : duTrace) {
            r.append(s);
            if (s.length() > 0) {
                r.append(", ");
            }
        }
        // remove last ", "
        String traceString = r.toString();
        if (traceString.length() > 2) {
            return traceString.substring(0, traceString.length() - 2);
        }
        return traceString;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        StringBuffer ret = new StringBuffer();
        for (MethodCall m : finishedCalls) {
            ret.append(m);
        }
        ret.append("\nCovered methods: ");
        for (Entry<String, Integer> entry : coveredMethods.entrySet()) {
            ret.append(entry.getKey() + ": " + entry.getValue() + ", ");
        }
        ret.append("\nCovered predicates: ");
        for (Entry<Integer, Integer> entry : coveredPredicates.entrySet()) {
            ret.append(entry.getKey() + ": " + entry.getValue() + ", ");
        }
        ret.append("\nTrue distances: ");
        for (Entry<Integer, Double> entry : trueDistances.entrySet()) {
            ret.append(entry.getKey() + ": " + entry.getValue() + ", ");
        }
        ret.append("\nFalse distances: ");
        for (Entry<Integer, Double> entry : falseDistances.entrySet()) {
            ret.append(entry.getKey() + ": " + entry.getValue() + ", ");
        }
        return ret.toString();
    }

    /**
     * Adds trace information to the active MethodCall in this.stack
     */
    private void updateTopStackMethodCall(int branch, int bytecode_id, double true_distance, double false_distance) {

        if (traceCalls) {
            if (stack.isEmpty()) {
                return;
            }
            stack.peek().branchTrace.add(branch); // was: bytecode_id
            stack.peek().trueDistanceTrace.add(true_distance);
            stack.peek().falseDistanceTrace.add(false_distance);
            assert ((true_distance == 0.0) || (false_distance == 0.0));
            // TODO line_trace ?
            if (ArrayUtil.contains(Properties.CRITERION, Criterion.DEFUSE)
                    || ArrayUtil.contains(Properties.CRITERION, Criterion.ALLDEFS)) {
                stack.peek().defuseCounterTrace.add(duCounter);
            }
        }
    }

    /**
     * {@inheritDoc}
     * <p>
     * Adds Definition-Use-Coverage trace information for the given use.
     * <p>
     * Registers the given caller-Object Traces the occurrence of the given use
     * in the passedUses-field
     */
    @Override
    public void usePassed(Object object, Object caller, int useID) {

        if (!traceCalls) // TODO ???
            return;

        Use use = DefUsePool.getUseByUseId(useID);

        int objectID = registerObject(caller);
        // if this is a static variable, treat objectID as zero for consistency
        // in the representation of static data
        if (objectID != 0) {
            if (use == null)
                throw new IllegalStateException(
                        "expect DefUsePool to known defIDs that are passed by instrumented code");
            if (use.isStaticDefUse())
                objectID = 0;
        }
        String varName = use.getVariableName();
        if (passedUses.get(varName) == null) {
            passedUses.put(varName, new HashMap<>());
            passedUseObject.put(varName, new HashMap<>());
        }

        HashMap<Integer, Integer> uses = passedUses.get(varName).get(objectID);
        HashMap<Integer, Object> usesObject = passedUseObject.get(varName).get(objectID);
        if (uses == null) {
            uses = new HashMap<>();
            usesObject = new HashMap<>();
        }

        uses.put(duCounter, useID);
        usesObject.put(duCounter, object);
        passedUses.get(varName).put(objectID, uses);
        passedUseObject.get(varName).put(objectID, usesObject);
        duCounter++;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#wasMutationTouched(int)
     */

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean wasMutationTouched(int mutationId) {
        return touchedMutants.contains(mutationId);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<Integer, Double> getFalseDistancesSum() {
        return falseDistancesSum;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<Integer, Double> getTrueDistancesSum() {
        return trueDistancesSum;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Map<String, HashMap<Integer, HashMap<Integer, Integer>>> getPassedUses() {
        return passedUses;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getPassedDefIDs()
     */
    // Map<String, HashMap<Integer, HashMap<Integer, Integer>>>

    @Override
    public Set<Integer> getPassedDefIDs() {
        Set<Integer> defs = new HashSet<>();
        for (HashMap<Integer, HashMap<Integer, Integer>> classDefs : passedDefinitions.values()) {
            for (HashMap<Integer, Integer> currentDefs : classDefs.values()) {
                defs.addAll(currentDefs.values());
            }
        }
        return defs;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getPassedUseIDs()
     */
    @Override
    public Set<Integer> getPassedUseIDs() {
        Set<Integer> uses = new HashSet<>();
        for (HashMap<Integer, HashMap<Integer, Integer>> classUses : passedUses.values()) {
            for (HashMap<Integer, Integer> currentUses : classUses.values()) {
                uses.addAll(currentUses.values());
            }
        }
        return uses;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getTrueDistancesContext()
     */
    @Override
    public Map<Integer, Map<CallContext, Double>> getTrueDistancesContext() {
        return coveredTrueContext;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getFalseDistancesContext()
     */
    @Override
    public Map<Integer, Map<CallContext, Double>> getFalseDistancesContext() {
        return coveredFalseContext;
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.evosuite.testcase.ExecutionTrace#getPredicateContextExecutionCount()
     */
    @Override
    public Map<Integer, Map<CallContext, Integer>> getPredicateContextExecutionCount() {
        return coveredPredicateContext;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.evosuite.testcase.ExecutionTrace#getMethodContextCount()
     */
    @Override
    public Map<String, Map<CallContext, Integer>> getMethodContextCount() {
        return coveredMethodContext;
    }

    /**
     * This set keeps those classes that have a static write (i.e. PUTSTATIC)
     * during test execution.
     */
    private HashSet<String> classesWithStaticWrites = new HashSet<>();

    @Override
    public void putStaticPassed(String classNameWithDots, String fieldName) {
        classesWithStaticWrites.add(classNameWithDots);
    }

    /**
     * This set keeps those classes that have a static read (i.e. GETSTATIC)
     * during test execution.
     */
    private HashSet<String> classesWithStaticReads = new HashSet<>();

    @Override
    public void getStaticPassed(String classNameWithDots, String fieldName) {
        classesWithStaticReads.add(classNameWithDots);
    }

    @Override
    public Set<String> getClassesWithStaticWrites() {
        return classesWithStaticWrites;
    }

    /**
     * This field keeps the names of those classes that were initialized (ie
     * <clinit> was completed during this test execution). The list has no
     * repetitions.
     */
    private List<String> initializedClasses = new LinkedList<>();

    /**
     * Adds the class to the list of those classes that were initialized during
     * this test execution. The class is not added if it was already contained
     * in the list.
     */
    @Override
    public void classInitialized(String classNameWithDots) {
        if (!initializedClasses.contains(classNameWithDots)) {
            initializedClasses.add(classNameWithDots);
        }
    }

    @Override
    public Set<String> getClassesWithStaticReads() {
        return classesWithStaticReads;
    }

    @Override
    public List<String> getInitializedClasses() {
        return this.initializedClasses;
    }
}
